﻿using MongoDB.Driver;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.ESG.Entities;
using Sustainalytics.ESGRatings.BusinessLogic;
using Sustainalytics.ESGRatings.BusinessLogic.ESGClient;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace Sustainalytics.ReportGenerator.BL.Tests
{
    public class ReportDataPreparationSyncStepTests
    {
        private readonly ITestOutputHelper _output;

        public ReportDataPreparationSyncStepTests(ITestOutputHelper output)
        {
            _output = output;
        }

        [Fact]
        public void ReportDataPreparationSyncStepTest()
        {
            var context = TestSyncContextFactoy.Create();

            var step = new ReportDataPreparationSyncStep();
            var result = step.Start(context);

            foreach (var line in result.Report)
            {
                _output.WriteLine(line);
            }

            Assert.True(result != null);
        }

        [Fact]
        public void AssessmentTest()
        {
            var syncContext = TestSyncContextFactoy.Create();


            string esgComputeAddressString;
            if (!syncContext.GAConnectionStrings.TryGetValue("ComputationServiceAddress", out esgComputeAddressString))
                throw new ArgumentException("ComputationServiceAddress key is missing!");

            string clientDWConnectionString;
            if (!syncContext.GAConnectionStrings.TryGetValue("ClientDW", out clientDWConnectionString))
                throw new ArgumentException("ClientDW key is missing!");

            string clientUniversesConnectionString;
            if (!syncContext.GAConnectionStrings.TryGetValue("ClientUniverses", out clientUniversesConnectionString))
                throw new ArgumentException("ClientUniverses key is missing!");

            string clientPlatformConnectionString;
            if (!syncContext.GAConnectionStrings.TryGetValue("ClientPlatform", out clientPlatformConnectionString))
                throw new ArgumentException("ClientPlatform key is missing!");


            string dwArchiveConnectionString;
            if (!syncContext.GAConnectionStrings.TryGetValue("DWArchive", out dwArchiveConnectionString))
                throw new ArgumentException("DWArchive key is missing!");

            var esgRatingsCache = new ESGRatingsComputationsClient(esgComputeAddressString, new ESGProfile { CompanyType = CompanyTypeEnum.Combined, ComparativeGroup = ComparativeGroupEnum.PeerGroup, TemplateType = TemplateTypeEnum.Combined, MatrixId = Guid.Empty, ReferenceUniverseId = new Guid("71f2972e-3153-4f83-ac6e-d82bd676481f") }, new Guid("09e0bb9a-33af-4601-96f0-db1926beab88"));

            var esgRatingsTask = esgRatingsCache.GetRatingsAsync();

            var companiesWithEsgResearchForProfile = new List<Guid> { new Guid("3f911836-3958-4ce0-85be-23369bcf7793") };

            var financialDataForProfile = GetCompanyFinancialData(MongoFactory.GetCollectionFromDatabase<FinancialData>(clientDWConnectionString), companiesWithEsgResearchForProfile).Result;


            UpdateFinancialData(syncContext, esgRatingsTask, financialDataForProfile).Wait();

            var exceptions = new List<Exception>();

            Func<RPCompanyESGComputationResult, Task> computation = (x) =>
            {
                return Task.Factory.StartNew(() => _output.WriteLine(x.Dump(10)));
            };


            Parallel.ForEach(companiesWithEsgResearchForProfile, new ParallelOptions { CancellationToken = syncContext.Token, MaxDegreeOfParallelism = 8 }, (companyId) =>
            {
                GetEsgData(syncContext, esgRatingsTask, computation, exceptions, companyId).Wait();

            });

        }

        private static async Task UpdateFinancialData(SyncContext syncContext, Task<ESGRatingsAdapter> esgRatingsTask, Dictionary<Guid, FinancialData> financialData)
        {
            try
            {
                var clientDWConnectionString = syncContext.GAConnectionStrings["ClientDW"];
                var thresholdsmanager = new EsgThresholdManager<EsgFocusThresholds, string>(clientDWConnectionString);
                var taskResult = await esgRatingsTask.ConfigureAwait(false);
                var treshholds = thresholdsmanager.GetCompanyEsgThresholds();
                var count = taskResult.LoadFinancialData(financialData);
                taskResult.LoadEsgFocus(treshholds);
                syncContext.Log(string.Format("Updated AllFinancialData for {0} records", count));
            }
            catch (AggregateException ex)
            {
                syncContext.Log(ExceptionUtility.GetInfo(ex, true));
                throw;
            }
            catch (Exception ex)
            {
                syncContext.Log(ExceptionUtility.GetInfo(ex, true));
                throw;
            }
        }

        private static async Task<Dictionary<Guid, FinancialData>> GetCompanyFinancialData(IMongoCollection<FinancialData> financialDataCollection, List<Guid> companiesWithEsgResearch)
        {
            var financialData = await financialDataCollection.Find(x => companiesWithEsgResearch.Contains(x.Id)).ToListAsync();
            return financialData.ToDictionary(x => x.Id);
        }


        private static async Task GetEsgData(SyncContext syncContext, Task<ESGRatingsAdapter> esgRatingsTask, Func<RPCompanyESGComputationResult, Task> persistanceAction, List<Exception> exceptions, Guid companyId)
        {
            try
            {
                var esgRatingsData = await esgRatingsTask;
                var computationResult = new RPCompanyESGComputationResult { Id = companyId, SetId = esgRatingsData.SetId };

                var esgData = esgRatingsData.GetEsgData(companyId);

                if (esgData == null)
                {
                    syncContext.Log($"Computation data not found for company {companyId}");
                }
                else
                {
                    computationResult.IsInReferenceUniverse = esgData.IsInReferenceUniverse;
                    computationResult.OverallPerformance = esgRatingsData.GetOverallPerformance(companyId);
                    computationResult.EnvironmentPerformance = esgRatingsData.GetEnvironmentPerformance(companyId);
                    computationResult.SocialPerformance = esgRatingsData.GetSocialPerformance(companyId);
                    computationResult.GovernancePerformance = esgRatingsData.GetGovernancePerformance(companyId);
                    computationResult.DisclosureScore = esgRatingsData.GetDisclosurePerformance(companyId);
                    computationResult.PreparednessScore = esgRatingsData.GetPreparednessPerformance(companyId);
                    computationResult.QualitativePerformanceScore = esgRatingsData.GetQualitativePerformance(companyId);
                    computationResult.QuantitativePerformanceScore = esgRatingsData.GetQuantitativePerformance(companyId);

                    await persistanceAction(computationResult);
                }
            }
            catch (AggregateException ex)
            {
                syncContext.Log(ExceptionUtility.GetInfo(ex, true));
                exceptions.Add(ex);
            }
            catch (Exception ex)
            {
                syncContext.Log($"ReportDataPreparationSyncStep.Start: for {companyId} we got: {ex} ");
                exceptions.Add(ex);
            }
        }

    }
}
